#!/usr/bin/env python

# Copyright (c) 2009 by Alex Leone <acleone ~at~ gmail.com>
#
# This file is part of epp.
#
# epp is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# epp is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with epp.  If not, see <http://www.gnu.org/licenses/>.

import sys
import os
import shutil
import cPickle
from optparse import OptionParser
from epp import Epp

EPP_OUTPUT_EXT = '.epp'
EPP_NONOUTPUT_EXT = '.eppd'
EPP_MODTIMES_NAME = 'epp_modtimes.pkl'
EPP_INIT_NAME = '__epp_init.py'

def ensure_end_sep(dirpath):
    """
    Returns: dirpath with os.sep at the end
    """
    if not dirpath.endswith(os.sep):
        return dirpath + os.sep
    return dirpath

def needsupdate(infile, outfile):
    """
    Returns: False if outfile is newer than infile, True otherwise.
    """
    if not os.path.isfile(outfile):
        return True
    return os.path.getmtime(infile) > os.path.getmtime(outfile)

def nonoutput_modified(inname, infile, relpath_modtimes):
    """
    Returns: True if the nonoutput file 'infile' has been modified.
    """
    if relpath_modtimes == None:
        return True
    if inname in relpath_modtimes:
        return os.path.getmtime(infile) > relpath_modtimes[inname]
    return False



def main():
    """
    Returns: exit code
    """
    usage = "Usage: %prog [options] inputdir"
    optparser = OptionParser(usage)
    optparser.add_option("-o", "--output-dir", dest="outroot",
                        help="set the output directory to OUTROOT")
    (options, args) = optparser.parse_args()
    if len(args) == 0:
        inroot = '.'
    else:
        inroot = args[0]
    outroot = options.outroot
    inroot = ensure_end_sep(inroot)
    if not os.path.isdir(inroot):
        sys.stderr.write(
                "%r is not a valid input directory!\n"
                % (inroot))
        return os.EX_DATAERR

    if outroot == None:
        outroot = inroot

    parser = Epp()
    parser.setfileroots(inroot, outroot)

    epp_modtimesfile = os.path.join(inroot, EPP_MODTIMES_NAME)
    modtimes = None
    if os.path.isfile(epp_modtimesfile):
        mfile = open(epp_modtimesfile, 'rb')
        modtimes = cPickle.load(mfile)
        mfile.close()
    modtimesout = {}

    cwd = os.getcwd()
    for indir, dirs, files in os.walk(inroot):
        if EPP_INIT_NAME in files:
            print("found pre-build script %r" % (os.path.join(indir, EPP_INIT_NAME)))
            os.chdir(indir)
            execfile(EPP_INIT_NAME)
    os.chdir(cwd)

    prevdirs = {}
    initial_dir = (False, parser.gettags())
    for indir, dirs, files in os.walk(inroot):
        relpath = indir[len(inroot):]
        parser.setrelpath(relpath)
        #print("relpath=%r" % (relpath))
        outdir = os.path.join(outroot, relpath)
        if not os.path.isdir(outdir):
            os.makedirs(outdir)


        if modtimes != None and relpath in modtimes:
            relpath_modtimes = modtimes[relpath]
        else:
            relpath_modtimes = None
        relpath_modtimesout = {}
        modtimesout[relpath] = relpath_modtimesout

        if len(relpath) > 0:
            i = relpath.rfind(os.sep)
            if i == -1:
                pdir = ''
            else:
                pdir = relpath[:i]
            #print("pdir: %r" % pdir)
            prevdir_nonoutput_updated, prevdir_tags = prevdirs[pdir]
        else:
            prevdir_nonoutput_updated, prevdir_tags = initial_dir
        parser.settags(prevdir_tags.copy())

        # filename.epp - only parse and include to this dir and all sub dirs
        nonoutputs = []

        # filename.ext.epp - parse and output to filename.ext
        extoutputs = []

        # no filename.ext - just copy
        tocopy = []

        for inname in files:
            if not (len(relpath) == 0 and inname in (EPP_MODTIMES_NAME)):
                infile = os.path.join(indir, inname)
                if inname.endswith(EPP_OUTPUT_EXT):
                    outname = inname[:-len(EPP_OUTPUT_EXT)]
                    extoutputs.append((inname, infile, outname,
                                 os.path.join(outdir, outname)))
                elif inname.endswith(EPP_NONOUTPUT_EXT):
                    nonoutputs.append((inname, infile))
                elif inname not in (EPP_INIT_NAME):
                    tocopy.append((infile, os.path.join(outdir, inname)))

        if outdir != indir:
            # copy files that don't need to be parsed
            tocopy.sort()
            #print("tocopy: %s" % (tocopy))
            for infile, outfile in tocopy:
                if needsupdate(infile, outfile):
                    print("copying %r to %r" % (infile, outfile))
                    shutil.copy2(infile, outfile)

        # parse tags in nonoutputs
        nonoutput_updated = False
        nonoutputs.sort()
        #print("nonoutputs: %s" % (nonoutputs))
        for inname, infile in nonoutputs:
            relpath_modtimesout[inname] = os.path.getmtime(infile)
            if nonoutput_modified(inname, infile, relpath_modtimes):
                print("%s was updated!" % (infile))
                nonoutput_updated = True
            infilef = open(infile, 'r')
            parser.setfilenames(inname)
            outputstr = parser.parsefile(infilef)
            #if len(outputstr.strip()) > 0:
            #    print("Warning: nonoutput %r outputted something!" % (infile))
            infilef.close()

        update_outputs = nonoutput_updated or prevdir_nonoutput_updated
        prevtags = parser.gettags()
        # parse filename.ext.epp files to filename.ext
        extoutputs.sort()
        #print("extoutputs: %s" % (extoutputs))
        for inname, infile, outname, outfile in extoutputs:
            if update_outputs or needsupdate(infile, outfile):
                print("%s was updated!" % (infile))
                infilef = open(infile, 'r')
                parser.settags(prevtags.copy())
                parser.setfilenames(inname, outname)
                outputstr = parser.parsefile(infilef)
                parser.settags(prevtags)
                infilef.close()
                outfilef = open(outfile, 'w')
                outfilef.write(outputstr)
                outfilef.close()

        prevdirs[relpath] = (update_outputs, prevtags)

    mfile = open(epp_modtimesfile, 'wb')
    cPickle.dump(modtimesout, mfile)
    mfile.close()
    return os.EX_OK

if __name__ == "__main__":
    sys.exit(main())